package cz.mff.curreco.utils;


import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

import org.apache.tapestry5.beaneditor.Width;

import cz.mff.curreco.services.ImageService;


public class NormalizePicture {

	public static class ColorRange
	{
		public ColorRange()
		{
			rgb=0;
			distance=0;
			r=g=b=0;
		};
		public int rgb;
		public int distance;
		
		public int r;
		public int g;
		public int b;
		
	}
	public static class NPoint
	{
		public NPoint()
		{
			x=0;
			y=0;
		}
		public NPoint(int xAxis, int yAxis, ColorRange color)
		{
			x=xAxis;
			y=yAxis;
			this.useColorRange=color;
		}
		public int x;
		public int y;
		ColorRange useColorRange;

	}

	public static class Section
	{
		int starts;
		int ends;
		int count;
	}
	
	public static BufferedImage removeTrasparentPoints(BufferedImage img)
	{
		
		int x,y;
		int minx=0,maxx=img.getWidth()-1,miny=0,maxy=img.getHeight()-1;
		boolean invisible=true;
		y= miny;

		try{
			while (invisible && y<=maxy)
			{
				for(x=minx;x<=maxx;x++)
				{
					if (img.getRGB(x, y)<0)
					{
						invisible = false;
						break;
					}
				}
				if (invisible)
					y++;
			}

		}catch (Exception e) {
			y--;
		}

		miny= y;
		y=maxy;
		invisible=true;
		while (invisible && y>=miny )
		{
			invisible = true;
			for(x=minx;x<=maxx;x++)
			{
				if (img.getRGB(x, y)<0)
				{
					invisible = false;
					break;
				}
			}
			if (invisible)
				y--;
		}
		maxy=y;
		x=minx;
		invisible=true;
		while (invisible && x<=maxx)
		{
			invisible = true;
			for(y=miny;y<=maxy;y++)
			{
				if (img.getRGB(x, y)<0)
				{
					invisible = false;
					break;
				}
			}
			if (invisible)
				x++;
		}
		minx = x;
		x=maxx;

		invisible=true;
		while (invisible && x>=minx)
		{
			invisible = true;
			for(y=miny;y<=maxy;y++)
			{
				if (img.getRGB(x, y)<0)
				{
					invisible = false;
					break;
				}
			}
			if (invisible)
				x--;
		}
		maxx=x;
		BufferedImage result = img.getSubimage(minx, miny, maxx-minx, maxy-miny);
		return result;
		
	}
	
	public static BufferedImage Normalize(BufferedImage input)
	{
		
		
		BufferedImage img =  removeTrasparentPoints(input);
		
		//najprv odstranime pozadie
		BufferedImage img2 = removeBackGround(img);
		
		
		
		return img2;
	}
	
	
	public static BufferedImage Normalize2(BufferedImage input)
	{
		
		// osdtranenie prehladnych bodov 
		BufferedImage img =  removeTrasparentPoints(input);
		BufferedImage imgBackup = ImageUtils.cloneImage(img);
		//najprv odstranime pozadie
		BufferedImage img2 = removeBackGround2(img);
		
		Section xSection = new Section();
		Section ySection = new Section();
		findCircleBoundaries(img2,xSection,ySection);
		int x,y;
		for (x=xSection.starts;x<=xSection.ends;x++)
			img2.setRGB(x, ySection.starts, ImageUtils.createRGBColor(255, 0, 0));
		
		for (x=xSection.starts;x<=xSection.ends;x++)
			img2.setRGB(x, ySection.ends, ImageUtils.createRGBColor(255, 0, 0));
		for (y=ySection.starts;y<=ySection.ends;y++)
			img2.setRGB(xSection.starts, y, ImageUtils.createRGBColor(255, 0, 0));
		for (y=ySection.starts;y<=ySection.ends;y++)
			img2.setRGB(xSection.ends, y, ImageUtils.createRGBColor(255, 0, 0));
		
		BufferedImage result1 = imgBackup.getSubimage(xSection.starts, ySection.starts, xSection.ends- xSection.starts, ySection.ends - ySection.starts);
		//int maxSize = (result1.getWidth()>result1.getHeight())?result1.getWidth():result1.getHeight();
		int maxSize=200;
		BufferedImage finalResult = new BufferedImage(maxSize, maxSize, BufferedImage.TYPE_3BYTE_BGR);
		Graphics2D g = finalResult.createGraphics();
		g.drawImage(result1, 0, 0, maxSize, maxSize, null);
		g.dispose();
			
		
		return finalResult;
	}
	public static void findCircleBoundaries(BufferedImage img,Section xSection,Section ySection)
	{
		int width = img.getWidth();
		int height = img.getHeight();
		int [] rows= new int[height];
		int [] cols=new int [width];
		int x,y;
		boolean lastFound ; 
		int rgb;
		for (y = 0; y < height; y++) 
		{
			lastFound =false; 
			for (x=0;x<width;x++)
			{
				rgb= ImageUtils.getRGB(img, x, y) & 0x0000FF;
				if (rgb!=0x0000FF)
				{
					/*if (!lastFound)
						lastFound=true;
					else*/
						rows[y]++;
				}else
					lastFound=false;
			}
		}
		for (x=0;x<width;x++)
		{
			lastFound =false; 
			for (y = 0; y < height; y++) 
			{
				rgb= ImageUtils.getRGB(img, x, y) & 0x0000FF;
				if (rgb!=0x0000FF)
				{
					/*if (!lastFound)
						lastFound=true;
					else*/
						cols[x]++;
				}else
					lastFound=false;
			}
		}
		int maxxCount, maxyCount;
		maxxCount=maxyCount=0;
		LinkedList<Section> rowsSections = new LinkedList<Section>();
		LinkedList<Section> colsSections = new LinkedList<Section>();
		Section maxRowSection = null;
		Section maxColSection = null;
		Section lastSection = null;
		for (y = 0; y < height; y++) 
		{
			if (rows[y] > 0)
			{
				if (lastSection==null)
				{
					lastSection = new Section();
					lastSection.starts=y;
					rowsSections.add(lastSection);
				}
				lastSection.ends=y;
				lastSection.count+=rows[y];
				if (maxyCount<lastSection.count)
				{
					maxyCount=lastSection.count;
					maxRowSection=lastSection;
				}
			}else
			{
				lastSection = null;
			}
		}
		lastSection = null;
		for (x = 0; x < width; x++) 
		{
			if (cols[x] > 0)
			{
				if (lastSection==null)
				{
					lastSection = new Section();
					lastSection.starts=x;
					rowsSections.add(lastSection);
				}
				lastSection.ends=x;
				lastSection.count+=cols[x];
				if (maxxCount<lastSection.count)
				{
					maxxCount=lastSection.count;
					maxColSection=lastSection;
				}
			}else
			{
				lastSection = null;
			}
		}
		xSection.starts=maxColSection.starts;
		xSection.ends=maxColSection.ends;
		xSection.count = maxColSection.count;
		ySection.starts= maxRowSection.starts;
		ySection.ends= maxRowSection.ends;
		ySection.count = maxRowSection.count;
		
	}
	
	
	public static void removeNoise(BufferedImage img)
	{
		int x,y;
		int width = img.getWidth();
		int height = img.getHeight();
		for (x=0;x<img.getWidth();x++)
			for (y = 0; y < img.getHeight(); y++) 
			{
				checkAndRemoveNoise(x, y, width, height, img);
			}
	}
	
	private static void checkAndRemoveNoise(int x, int y, int width,int height,BufferedImage img )
	{
		int rgb= ImageUtils.getRGB(img, x, y) & 0x0000FF;
		if (rgb==0x0000FF)
			return;
		int minx = (x>0)?0:x-1;
		int miny = (y>0)?0:y-1;
		int maxx = (x+1<width)?x+1:width-1;
		int maxy = (y+1<height)?y+1:height-1;
		int count= 0;
		for (int i = minx; i <= maxx; i++) 
		{
			for (int j = miny; j <= maxy; j++) 
			{
				rgb= ImageUtils.getRGB(img, i, j) & 0x0000FF;
				if (rgb!=0x0000FF)
					count++;
			}
		}
		if (count<3)
			img.setRGB(x, y, ImageUtils.createRGBColor(0,0, 255));
	}
	
	private static BufferedImage removeBackGround2(BufferedImage img)
	{
		BufferedImage edgeImg = ImageService.staticEdgeDetection(img);
		int x,y;
		int width = img.getWidth();
		int height= img.getHeight();
		int r,g,b,rgb;
		for (x=0;x<width;x++)
			for (y=0;y<height;y++)
			{
				rgb=ImageUtils.getRGB(edgeImg, x, y);
				r=rgb>>16;
				g=rgb>>8 &255;
				b=rgb&255;
				if (r<128 && g<128 && b < 128)
				{
					img.setRGB(x,y,ImageUtils.createRGBColor(0, 0, 255));
				}				
			}
		for (x=0;x<width;x++)
			for (y=0;y<height;y++)
			{
				rgb=ImageUtils.getRGB(edgeImg, x, y);
				r=rgb>>16;
				g=rgb>>8 &255;
				b=rgb&255;
				if (r<128 && g<128 && b < 128)
				{
					img.setRGB(x,y,ImageUtils.createRGBColor(0, 0, 255));
				}				
			}
		return img;
	}
	private static BufferedImage removeBackGround(BufferedImage img)
	{
		int width = img.getWidth();
		int height = img.getHeight();
		BufferedImage edgeImg = ImageService.staticEdgeDetection(img);
		// maximalny mozny polomer mince na obrazku
		int radius = Math.min(width,height)/2;
		
		// v rohoch obrazku chceme "vzorove" stvroce pre pozadie s rovnakou velkostou
		// ich ulhopriecku si oznacme "uhl"
		// vzdialenost od stredu mince k bodu [0,0] ozacme maxDist
		// r je polomer mince, (ozacme odmocninu ako odm(x) )
		// r^2+r^2= maxDist^2 -> maxDist= odm(2)*r
		// potom ulh = MaxDist - r =  odm(2)*r - r = (odm(2) - 1)*r
		// co je priblizne ulh=0.414*r
		// mame maximalnu moznu uhlopriecku. Strana hladaneho stvorca (oznacme edg)
		// z 2*edg^2 = ulh^2  je edg = uhl/odm(2)=(odm(2) - 1)*r / odm(2) 
		// a to je priblizne edg=0.292893*r, 
		// dajme tam rezervu a nechajme to na 0.2*r
		int edg = Math.round(radius * 0.2f); 
		
		// potrebujeme zistit pozadie v rohoch
		ColorRange c00 ;
		ColorRange c01 ;
		ColorRange c10 ;
		ColorRange c11 ;
		int rgb;
		BufferedImage result = ImageUtils.cloneImage(img);

		c00 =getColorCenter(img,       0,   edg,         0,   edg);
		c01 =getColorCenter(img,width-edg,width,         0,   edg);
		c10 =getColorCenter(img,       0,   edg,height-edg,height);
		c11 =getColorCenter(img,width-edg,width,height-edg,height);

		int x,y;
		long[]mx = new long[width];
		long[]my = new long[height];
		// using array is faster than calculating
		for (x=0;x<width;x++)
			mx[x]=width -x;
		for (y=0;y<height;y++)
			my[y]= height-y;
		int centralRGB;
		int centralDistance;
		long widthXheight = (width-1) * (height-1);
		int r,g,b;
		int dr,dg,db;

		LinkedList<NPoint> queue = new LinkedList<NPoint>();
		
		ColorRange cl00=calculateBGColor(     0,    0,mx[0],my[0], widthXheight,c00,c01,c10,c11);
		ColorRange cl01=calculateBGColor(     0,my[0],mx[0],    0, widthXheight,c00,c01,c10,c11);
		ColorRange cl10=calculateBGColor( mx[0],    0,    0,my[0], widthXheight,c00,c01,c10,c11);
		ColorRange cl11=calculateBGColor( mx[0],my[0],    0,    0, widthXheight,c00,c01,c10,c11);
		queue.add(new NPoint(0,0,cl00));
		queue.add(new NPoint(width-1,0,cl10));
		queue.add(new NPoint(0,height-1,cl01));
		queue.add(new NPoint(width-1,height-1,cl11));
		NPoint n;
		ColorRange lastColor;
		boolean colorIsBgColor = false;
		while (queue.size()>0)
		{
			colorIsBgColor =false;
			n=queue.poll();
			x=n.x;
			y=n.y;
			lastColor=n.useColorRange;
			rgb = ImageUtils.getRGB(img, x, y);
			if (rgb==0)
				continue;
			
			colorIsBgColor=colorIsInRange(rgb,lastColor);
			if (!colorIsBgColor)
			{
				lastColor=calculateBGColor(x,y, mx[x],  my[y], widthXheight, c00, c01, c10, c11);
				colorIsBgColor=colorIsInRange(rgb,lastColor);
			}
			
			if ( colorIsBgColor )
			{
				rgb=ImageUtils.getRGB(edgeImg, x, y);
				r=rgb>>16;
				g=rgb>>8 &255;
				b=rgb&255;
				if (r<128 && g<128 && b < 128)
				{
					img.setRGB(x,y,0);
					if ((x+1)<width && ImageUtils.getRGB(img,x+1, y) >0)
						queue.add(new NPoint(x+1, y,lastColor));
					if ((x>0) && ImageUtils.getRGB(img,x-1, y) >0)
						queue.add(new NPoint(x-1, y,lastColor));
					if ((y+1<height)&&ImageUtils.getRGB(img,x, y+1) >0)
						queue.add(new NPoint(x, y+1,lastColor));
					if ((y>0)&&ImageUtils.getRGB(img,x, y-1) >0)
						queue.add(new NPoint(x, y-1,lastColor));
				}
			}
		}

		return img;
	}
	private static boolean colorIsInRange( int imgRGB, ColorRange bgRgb )
	{
		int r=imgRGB>>16;
		int g=imgRGB>>8 &255;
		int b=imgRGB&255;
		int dr=bgRgb.r-r;
		int dg=bgRgb.g-g;
		int db=bgRgb.b-b;
		if ( ( dr*dr + dg*dg + db*db)<=(bgRgb.distance*bgRgb.distance) )
			return true;
		return false;
	}
	
	private static ColorRange calculateBGColor(long x,long y, long mx, long my, long max,ColorRange c00,ColorRange c01,ColorRange c10,ColorRange c11)
	{
		ColorRange result = new ColorRange();
		result.r = calculateValue(x,y,mx,my,max,c00.r,c01.r,c10.r,c11.r);
		result.g = calculateValue(x,y,mx,my,max,c00.g,c01.g,c10.g,c11.g);
		result.b = calculateValue(x,y,mx,my,max,c00.b,c01.b,c10.b,c11.b);
		result.distance = calculateValue(x,y,mx,my,max,c00.distance,c01.distance,c10.distance,c11.distance);
		result.rgb= result.r<<16 | result.g <<8 | result.b;
		return result;
		
	}
	private static int calculateValue(long x,long y, long mx, long my,long max, int v00, int v01, int v10, int v11)
	{
		return (int)(((v00 * my + v01*y)*mx+(v10  * my +v11*y)*x)/max);
		
		
	}

	private static ColorRange getColorCenter(BufferedImage img,int minx,int maxx,int miny,int maxy)
	{
		ColorRange result = new ColorRange();
		int [] spectrum = new int [0x1000000];
		addSpectrum(img,     minx,   maxx,         miny,   maxy,spectrum);
		getTypycalColor(spectrum,result);
		return result;
	}
	
	private static void addSpectrum(BufferedImage img, int minx,int maxx,int miny,int maxy,int[] spectrum)
	{
		int x,y;
		for (x=minx;x<maxx;x++)
			for (y=miny;y<maxy;y++)
			{
				int rgb =0;
				rgb =ImageUtils.getRGB(img,x, y);
				spectrum[rgb]++;
			}
	}
	
	private static void getTypycalColor(int [] spectrum,ColorRange result)
	{
		int i;
		int r,g,b;
		int minr,ming,minb;
		minr=ming=minb=255;
		int maxr,maxg,maxb;
		maxr=maxg=maxb=0;
		int max=0;
		int minCount;
		for (i=0;i<=0xFFFFFF;i++)
			if (max<spectrum[i]) max = spectrum[i];
		minCount =2;
		for (i=0;i<=0xFFFFFF;i++)
			if (spectrum[i]>=minCount) 
			{
				r= i>>16;
				g= i>>8 & 255;
				b= i& 255;
				if (r<minr) minr=r;
				if (r>maxr) maxr=r;
				if (b<minb) minb=b;
				if (b>maxb) maxb=b;
				if (g<ming) ming=g;
				if (g>maxg) maxg=g;
			}
		r=(maxr+minr)/2;
		g=(maxg+ming)/2;
		b=(maxb+minb)/2;
		int distance = 0;
		if (distance < (maxr-minr)) distance = maxr-minr;
		if (distance < (maxg-ming)) distance = maxg-ming;
		if (distance < (maxb-minb)) distance = maxb-minb;
		//distance = Math.round(distance * 1.42f);
		distance = Math.round(distance * 2);
		
		//result.rgb= ImageUtils.createRGBColor(r, g, b);
		result.r= r;
		result.g= g;
		result.b= b;
		
		result.rgb= r<<16| g<<8| b;
		result.distance = distance;
		
	}
	
	
	
	
	
}
